package org.finesys.monitor.config;

import static org.springframework.http.HttpMethod.DELETE;
import static org.springframework.http.HttpMethod.POST;

import java.util.UUID;

import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import de.codecentric.boot.admin.server.config.AdminServerProperties;

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfig {

    private final String adminContextPath;
    private final AdminServerProperties adminServer;
    private final SecurityProperties securityProperties;

    public WebSecurityConfig(AdminServerProperties adminServerProperties, SecurityProperties securityProperties) {
        this.adminContextPath = adminServerProperties.getContextPath();
        this.adminServer = adminServerProperties;
        this.securityProperties = securityProperties;
    }

    /**
     * spring security 默认的安全策略
     *
     * @param httpSecurity security注入点
     * @return SecurityFilterChain
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(adminContextPath + "/");
        httpSecurity.headers().frameOptions().disable()
                .and().authorizeRequests().antMatchers(adminContextPath + "/assets/**",
                        adminContextPath + "/login", adminContextPath + "/instances/**", adminContextPath + "/actuator/**").permitAll()
                .anyRequest().authenticated().and()
                .formLogin().loginPage(adminContextPath + "/login")
                .successHandler(successHandler)
                .and().logout().logoutUrl(adminContextPath + "/logout")
                .and().httpBasic()
                .and().csrf().disable();
        //增加csrf过滤器功能
        httpSecurity.addFilterAfter(new CustomCsrfFilter(), BasicAuthenticationFilter.class)
                .csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
//                        .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
                        .ignoringRequestMatchers(
                                new AntPathRequestMatcher(this.adminServer.path("/instances"), POST.toString()),
                                new AntPathRequestMatcher(this.adminServer.path("/instances/*"), DELETE.toString()),
                                new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))
                        ));
        //记住我
        httpSecurity.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));
        return httpSecurity.build();
    }

    /**
     * 用户增加
     */
    @Bean
    public InMemoryUserDetailsManager userDetailsManager(PasswordEncoder passwordEncoder) {
        UserDetails userDetails = User.withUsername(securityProperties.getUser().getName())
                .password(passwordEncoder.encode(securityProperties.getUser().getPassword()))
                .roles("USER").build();
        return new InMemoryUserDetailsManager(userDetails);
    }

    /**
     * 密码加解密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
